package com.pine.app.module.security.oauth.provider.sso.token;

import com.pine.app.module.security.core.common.AuthConstant;
import com.pine.app.module.security.core.common.enums.ErrorType;
import com.pine.app.module.security.core.common.enums.GrantType;
import com.pine.app.module.security.oauth.config.properties.OAuth2SsoProperties;
import com.pine.app.module.security.oauth.exception.AuthenticationException;
import com.pine.app.module.security.oauth.exception.AuthenticationServerException;
import com.pine.app.module.security.oauth.provider.DefaultOAuth2RefreshToken;
import com.pine.app.module.security.oauth.provider.token.OAuth2AccessToken;
import com.pine.app.module.security.oauth.provider.token.SimpleOAuth2AccessEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClientException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 *   授权码模式的处理器
 * @author xiaoyuan
 * @create 2020/3/17 20:52
 **/
@Slf4j
public class AuthorizationCodeSsoTokenHandler extends AbstractOAuth2SsoTokenHandler {


    public AuthorizationCodeSsoTokenHandler(OAuth2SsoProperties oAuth2ClientProperties) {
        super(oAuth2ClientProperties);
    }



    /**
     * @Description: 获取accessToken
     * @Author: xiaoyuan
     * @Date: 2020/3/18 18:56
     */
    @Override
    public OAuth2AccessToken getAccessToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
        boolean debugEnabled = log.isDebugEnabled();
        String code = request.getParameter(AuthConstant.code);
        String state = request.getParameter(AuthConstant.state);
        if (StringUtils.isBlank(code)) {

            Map<String, String> params = new HashMap<>();
            params.put(AuthConstant.client_id, this.getoAuth2ClientProperties().getClient_id());
            params.put(AuthConstant.redirect_uri, request.getRequestURL().toString());
            params.put(AuthConstant.response_type, "code");
            state = UUID.randomUUID().toString();
           // addState(state);
            request.getSession().setAttribute(PINE_STATE,state);
            params.put(AuthConstant.state, state);
            String url = this.buildUrl(getoAuth2ClientProperties().getUser_authorization_uri(), params);
            if(debugEnabled){
                log.debug("未登陆，跳转到认证服务器，跳转url：{}",url);
            }
            response.sendRedirect(url);
            return null;
        }
        Object stateStore = request.getSession().getAttribute(PINE_STATE);
        //此处是校验返回的state是否正确   防止非法请求
        if (stateStore==null|| StringUtils.isBlank(stateStore.toString())|| StringUtils.isBlank(state)|| !stateStore.equals(state)) {
            ErrorType.ILLEGAL_REQUEST.throwThis(AuthenticationException::new, "非法state，请重新登陆");
        }
        request.getSession().removeAttribute(PINE_STATE);
        MultiValueMap<String, String> formData = new LinkedMultiValueMap();
        formData.add(AuthConstant.grant_type, GrantType.AUTHORIZATION_CODE.getName());
        formData.add(AuthConstant.code, code);
        formData.add(AuthConstant.redirect_uri, request.getRequestURL().toString());
        formData.add(AuthConstant.scope, "all");
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", this.getAuthorizationHeader(getoAuth2ClientProperties().getClient_id(), getoAuth2ClientProperties().getClient_secret()));
        Map<String, Object> map = null;
        try {
            if(debugEnabled){
                log.debug("通过code获取token，url：{}",getoAuth2ClientProperties().getAccess_token_uri());
            }
            //通过code去获取accesstoken
            map = this.postForMap(getoAuth2ClientProperties().getAccess_token_uri(), formData, headers);
        } catch (ResourceAccessException e) {
            throw new AuthenticationServerException(ErrorType.SERVICE_ERROR, "认证服务器连接错误",null,null);
        }catch (RestClientException e){
            throw new AuthenticationException(ErrorType.SERVICE_ERROR, "认证错误",null,null);
        }
        if (map.containsKey("error") || Boolean.FALSE.equals(map.get("success"))) {
            if (debugEnabled) {
                this.log.debug("获取token接口出现错误");
            }
            throw new AuthenticationException(ErrorType.SERVICE_ERROR, "获取token接口出现错误",null,null);
        }
        //组装token实体类
        SimpleOAuth2AccessEntity simpleOAuth2AccessEntity = new SimpleOAuth2AccessEntity(map.get(AuthConstant.access_token).toString());
        simpleOAuth2AccessEntity.setExpireTime(new Date(System.currentTimeMillis() + ( Long.valueOf(map.get(AuthConstant.expire_in).toString())*1000)));
        if(map.get(AuthConstant.refresh_token)!=null){
            simpleOAuth2AccessEntity.setOauth2RefreshToken(new DefaultOAuth2RefreshToken(map.get(AuthConstant.refresh_token).toString(),null));
        }
        return simpleOAuth2AccessEntity;
    }

    //组装basic亲求头
    private String getAuthorizationHeader(String clientId, String clientSecret) {
        if (clientId == null || clientSecret == null) {
            this.log.warn("Null Client ID or Client Secret detected. Endpoint that requires authentication will reject request with 401 error.");
        }
        String creds = String.format("%s:%s", new Object[]{clientId, clientSecret});
        try {
            return "Basic " + new String(Base64.getEncoder().encode(creds.getBytes("UTF-8")));
        } catch (UnsupportedEncodingException var5) {
            throw new IllegalStateException("Could not convert String");
        }
    }
}
